#include "ekk_interface.h"
#include "ekk_io/loop_buf.h"

#define ENABLE_FIRST_BIG_BUF_IN_STACK 1
#if ENABLE_FIRST_BIG_BUF_IN_STACK
static char g_stack_mem[LOOP_BUF_SIZE] = {0};
static volatile ekk_bool g_stack_used = ekk_false;
#endif

signed long InitLoopBuf(loop_buf *buf, signed long size)
{	
	if ( !buf )
		return 1;
	ekk_memset(buf,0,sizeof(loop_buf));
	
	if (size == 0)
	{
		size = LOOP_BUF_SIZE;
	}
	
	buf->size = size;
	
#if ENABLE_FIRST_BIG_BUF_IN_STACK
	if (size == LOOP_BUF_SIZE && !g_stack_used)
	{
		g_stack_used = ekk_true;
		buf->buffer = g_stack_mem;
	}
#endif
	
	if ( !buf->buffer )
		buf->buffer = (char*)ekk_malloc(size);
	
	if ( !buf->buffer )
		return 1;
	 
	buf->in = buf->out = 0;
	 
	return 0;
}

signed long PutLoopBuf(loop_buf *buf, char *buffer, signed long len)
{
	signed long in,out,l1,l2,l,size;

	in = buf->in;
	out = buf->out;
	size = buf->size;

	//real length , left 1 byte for avoiding bug
	len = MIN(len, (in >= out)? (size - in + out -1 ): (out - in -1));
	
	//l1 for back part, l2 for front part
	l1 = len > (size - in) ? (size - in) : len;
	len -= l1;
	l2 = len>0 ? len : 0;

	//put data to back part of looping buffer
	if(l1>0)
	{
		ekk_memcpy(buf->buffer + in, buffer, l1); 
	}
	
	//put rest data to front part
	if(l2>0)
	{
		ekk_memcpy(buf->buffer, buffer + l1, l2);
	}  

	len = l1 + l2;
	//move offset
	l = in +len;
	l %= size;
	buf->in = l; 

	return len;
}

signed long GetLoopBuf(loop_buf *buf, char *buffer, signed long len)
{
	signed long in,out,l1,l2,l,size;
	in = buf->in;
	out = buf->out;
	size = buf->size;
	
	len = MIN(len, (in >= out) ? (in - out) : (size - out + in));

	l1 = len > (size - out) ? (size - out) : len;
	len -= l1;
	l2 = len > 0 ? len : 0;
	
	//put data to back part of looping buffer
	if(l1>0)
	{
		ekk_memcpy(buffer, buf->buffer + out, (unsigned int)l1); 
	}
	
	//put rest data to front part
	if(l2>0)
	{
		ekk_memcpy(buffer + l1, buf->buffer, (unsigned int)l2);
	}

	len = l1 + l2;

	l = out + len;
	l %= size;
	buf->out = l;

	return len;
}

signed long LoopBufSize(loop_buf *buf)
{
	return buf->size - 1;
}

void LoopBufReset(loop_buf *buf)
{
	buf->in = buf->out = 0;
}

signed long LoopBufLen(loop_buf *buf)
{
	signed long in,out,size;
	in = buf->in;
	out = buf->out;
	size = buf->size;
	return (in >= out)? (in - out) : (size - out + in);
}

void ReleaseLoopBuf(loop_buf *buf)
{
	if (!buf )
		return;
	
	if ( buf->buffer )
	{
#if ENABLE_FIRST_BIG_BUF_IN_STACK
		if (buf->buffer == g_stack_mem)
		{
			g_stack_used = ekk_false;
			return;
		}
#endif
		ekk_free(buf->buffer);
	}
} 
